import os
import shutil
from typing import Tuple
from git import Repo, InvalidGitRepositoryError, GitCommandError
from utils.logger import get_logger


class GitOperations:
    """Git操作类"""
    
    def __init__(self):
        self.logger = get_logger("GitOperations")
    
    def init_repository(self, repo_path: str) -> Tuple[bool, str]:
        """
        初始化Git仓库
        
        Args:
            repo_path: 仓库路径
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            # 检查是否已经是Git仓库
            if os.path.exists(os.path.join(repo_path, '.git')):
                self.logger.info(f"目录已经是Git仓库: {repo_path}")
                return True, "目录已经是Git仓库"
            
            # 初始化仓库
            repo = Repo.init(repo_path)
            
            # 保持Git的默认行为，不强制设置分支名
            # 让Git使用系统默认设置（通常是master）
            self.logger.info("使用Git默认分支设置")
            
            self.logger.info(f"成功初始化Git仓库: {repo_path}")
            return True, "Git仓库初始化成功"
            
        except Exception as e:
            error_msg = f"初始化Git仓库失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def add_remote(self, repo_path: str, remote_name: str, remote_url: str) -> Tuple[bool, str]:
        """
        添加远程仓库
        
        Args:
            repo_path: 本地仓库路径
            remote_name: 远程仓库名称 (通常是'origin')
            remote_url: 远程仓库URL
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            repo = Repo(repo_path)
            
            # 检查远程仓库是否已存在
            if remote_name in [remote.name for remote in repo.remotes]:
                # 更新远程仓库URL
                remote = repo.remote(remote_name)
                remote.set_url(remote_url)
                self.logger.info(f"更新远程仓库URL: {remote_name} -> {remote_url}")
                return True, f"更新远程仓库 '{remote_name}' URL成功"
            else:
                # 添加新的远程仓库
                repo.create_remote(remote_name, remote_url)
                self.logger.info(f"添加远程仓库: {remote_name} -> {remote_url}")
                return True, f"添加远程仓库 '{remote_name}' 成功"
                
        except InvalidGitRepositoryError:
            error_msg = f"不是有效的Git仓库: {repo_path}"
            self.logger.error(error_msg)
            return False, error_msg
        except Exception as e:
            error_msg = f"添加远程仓库失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def clone_repository(self, remote_url: str, local_path: str) -> Tuple[bool, str]:
        """
        克隆远程仓库
        
        Args:
            remote_url: 远程仓库URL
            local_path: 本地路径
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            # 如果目录已存在，先删除
            if os.path.exists(local_path):
                shutil.rmtree(local_path)
            
            self.logger.info(f"开始克隆仓库: {remote_url}")
            
            # 克隆仓库，设置超时时间
            import subprocess
            import threading
            import time
            
            # 使用subprocess进行克隆，以便设置超时
            def run_clone():
                try:
                    result = subprocess.run(
                        ['git', 'clone', remote_url, local_path],
                        capture_output=True,
                        text=True,
                        timeout=300  # 5分钟超时
                    )
                    return result.returncode == 0, result.stdout, result.stderr
                except subprocess.TimeoutExpired:
                    return False, "", "克隆操作超时（5分钟）"
                except Exception as e:
                    return False, "", str(e)
            
            # 在新线程中运行克隆操作
            clone_result = [None]
            clone_thread = threading.Thread(target=lambda: clone_result.__setitem__(0, run_clone()))
            clone_thread.daemon = True
            clone_thread.start()
            clone_thread.join(timeout=300)  # 等待最多5分钟
            
            if clone_thread.is_alive():
                self.logger.error("克隆操作超时")
                return False, "克隆操作超时，请检查网络连接"
            
            if clone_result[0] is None:
                return False, "克隆操作失败"
            
            success, stdout, stderr = clone_result[0]
            
            if success:
                self.logger.info(f"成功克隆仓库: {remote_url} -> {local_path}")
                return True, f"克隆仓库成功: {local_path}"
            else:
                error_msg = f"克隆仓库失败: {stderr}"
                self.logger.error(error_msg)
                return False, error_msg
            
        except Exception as e:
            error_msg = f"克隆仓库失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def add_and_commit(self, repo_path: str, commit_message: str = "Initial commit") -> Tuple[bool, str]:
        """
        添加所有文件并提交
        
        Args:
            repo_path: 仓库路径
            commit_message: 提交消息
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            repo = Repo(repo_path)
            
            # 创建或更新 .gitignore 文件
            self._create_gitignore(repo_path)
            
            # 添加所有文件
            repo.git.add(A=True)
            
            # 检查是否有文件需要提交
            # 对于新仓库（没有HEAD），直接检查是否有暂存的文件
            try:
                # 尝试检查与HEAD的差异
                staged_files = repo.index.diff("HEAD")
                untracked_files = repo.untracked_files
                
                if not staged_files and not untracked_files:
                    return True, "没有需要提交的文件"
                    
            except Exception:
                # 如果HEAD不存在（新仓库），检查是否有暂存的文件
                if not repo.index.entries and not repo.untracked_files:
                    return True, "没有需要提交的文件"
            
            # 提交
            commit = repo.index.commit(commit_message)
            
            # 确保有正确的分支（保持传统的master分支优先）
            try:
                # 如果这是第一次提交，默认保持master分支（Git传统）
                if not repo.heads:
                    # 第一次提交后，Git会自动创建当前分支
                    # 通常是master，我们保持这个传统
                    self.logger.info("首次提交完成，保持默认分支")
                # 不再自动重命名master为main，保持用户的选择
            except Exception as e:
                self.logger.warning(f"分支操作失败: {e}")
            
            self.logger.info(f"成功提交: {commit_message}")
            return True, f"提交成功: {commit_message}"
            
        except InvalidGitRepositoryError:
            error_msg = f"不是有效的Git仓库: {repo_path}"
            self.logger.error(error_msg)
            return False, error_msg
        except GitCommandError as e:
            error_msg = f"Git命令执行失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
        except Exception as e:
            error_msg = f"提交失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def _create_gitignore(self, repo_path: str):
        """创建 .gitignore 文件，过滤常见的不需要提交的文件"""
        import os
        
        gitignore_content = """# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/

# nyc test coverage
.nyc_output

# Grunt intermediate storage
.grunt

# Bower dependency directory
bower_components

# node_modules
node_modules/

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# Large files (>50MB)
*.zip
*.rar
*.7z
*.tar.gz
*.iso
*.dmg
*.exe
*.msi

# Media files that are typically large
*.mp4
*.avi
*.mkv
*.mov
*.wmv
*.flv
*.webm
*.m4v
"""
        
        gitignore_path = os.path.join(repo_path, '.gitignore')
        
        try:
            # 如果 .gitignore 不存在，创建它
            if not os.path.exists(gitignore_path):
                with open(gitignore_path, 'w', encoding='utf-8') as f:
                    f.write(gitignore_content)
                self.logger.info("创建 .gitignore 文件")
            else:
                # 如果存在，检查是否需要添加内容
                with open(gitignore_path, 'r', encoding='utf-8') as f:
                    existing_content = f.read()
                
                # 添加基本的Python忽略规则（如果不存在）
                if '__pycache__/' not in existing_content:
                    with open(gitignore_path, 'a', encoding='utf-8') as f:
                        f.write('\n# Auto-added Python ignores\n__pycache__/\n*.pyc\n*.pyo\n')
                    self.logger.info("更新 .gitignore 文件")
                    
        except Exception as e:
            self.logger.warning(f"创建/更新 .gitignore 失败: {e}")
    
    def push_to_remote(self, repo_path: str, remote_name: str = "origin", branch_name: str = None) -> Tuple[bool, str]:
        """
        推送到远程仓库
        
        Args:
            repo_path: 本地仓库路径
            remote_name: 远程仓库名称
            branch_name: 分支名称
            
        Returns:
            Tuple[bool, str]: (成功状态, 消息)
        """
        try:
            repo = Repo(repo_path)
            
            # 检查远程仓库是否存在
            if remote_name not in [remote.name for remote in repo.remotes]:
                return False, f"远程仓库 '{remote_name}' 不存在"
            
            remote = repo.remote(remote_name)
            
            # 获取本地分支列表
            local_branches = [head.name for head in repo.heads]
            self.logger.info(f"本地分支列表: {local_branches}")
            
            # 尝试推送策略：按优先级尝试不同分支
            branches_to_try = []
            
            # 如果指定了分支名，优先尝试
            if branch_name:
                branches_to_try.append(branch_name)
            
            # 尝试检测当前分支
            try:
                current_branch = repo.active_branch.name
                if current_branch not in branches_to_try:
                    branches_to_try.append(current_branch)
                self.logger.info(f"检测到当前分支: {current_branch}")
            except:
                self.logger.warning("无法检测当前分支")
            
            # 添加常见的分支名作为备选（按传统优先级：先master后main）
            common_branches = ["master", "main"]
            for branch in common_branches:
                if branch not in branches_to_try and branch in local_branches:
                    branches_to_try.append(branch)
            
            # 如果还没有找到分支，使用所有本地分支
            for branch in local_branches:
                if branch not in branches_to_try:
                    branches_to_try.append(branch)
            
            self.logger.info(f"将按顺序尝试推送分支: {branches_to_try}")
            
            last_error = None
            
            for try_branch in branches_to_try:
                try:
                    self.logger.info(f"尝试推送分支: {try_branch}")
                    
                    # 推送
                    push_info = remote.push(f"{try_branch}:{try_branch}")
                    
                    # 检查推送结果
                    success, message = self._check_push_result(push_info, try_branch)
                    if success:
                        return True, message
                    else:
                        last_error = message
                        self.logger.warning(f"分支 {try_branch} 推送失败: {message}")
                        continue
                        
                except Exception as e:
                    error_msg = f"推送分支 {try_branch} 时出错: {str(e)}"
                    self.logger.warning(error_msg)
                    last_error = error_msg
                    continue
            
            # 所有分支都推送失败
            return False, f"所有分支推送都失败，最后错误: {last_error}"
            
        except InvalidGitRepositoryError:
            error_msg = f"不是有效的Git仓库: {repo_path}"
            self.logger.error(error_msg)
            return False, error_msg
        except Exception as e:
            error_msg = f"Git推送失败: {str(e)}"
            self.logger.error(error_msg)
            return False, error_msg
    
    def _check_push_result(self, push_info, branch_name: str) -> Tuple[bool, str]:
        """检查推送结果"""
        try:
            # 检查推送结果
            success_count = 0
            error_count = 0
            
            for info in push_info:
                try:
                    # 检查错误标志
                    if hasattr(info, 'ERROR') and info.flags & info.ERROR:
                        error_msg = f"推送失败: {info.summary}"
                        self.logger.error(error_msg)
                        error_count += 1
                        continue
                    
                    # 检查是否已是最新状态
                    if hasattr(info, 'UP_TO_DATE') and info.flags & info.UP_TO_DATE:
                        self.logger.info("远程仓库已是最新状态")
                        success_count += 1
                        continue
                    
                    # 检查是否推送成功 - 兼容不同版本的GitPython
                    pushed_flags = []
                    if hasattr(info, 'PUSHED'):
                        pushed_flags.append(info.PUSHED)
                    if hasattr(info, 'NEW_HEAD'):
                        pushed_flags.append(info.NEW_HEAD)
                    if hasattr(info, 'FORCED_UPDATE'):
                        pushed_flags.append(info.FORCED_UPDATE)
                    
                    is_pushed = False
                    for flag in pushed_flags:
                        if info.flags & flag:
                            is_pushed = True
                            break
                    
                    if is_pushed:
                        success_count += 1
                        self.logger.info(f"成功推送到远程仓库: origin/{branch_name}")
                    else:
                        error_count += 1
                        self.logger.warning(f"推送状态未知: flags={info.flags}, summary={info.summary}")
                        
                except Exception as e:
                    self.logger.error(f"检查推送信息时出错: {e}")
                    error_count += 1
            
            if success_count > 0 and error_count == 0:
                return True, f"成功推送到远程仓库: origin/{branch_name}"
            elif error_count > 0:
                return False, f"推送部分失败: {error_count} 个错误"
            else:
                return False, "推送状态未知"
                
        except Exception as e:
            return False, f"检查推送结果失败: {str(e)}"
